﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace SharedFizzBuzz
{

    public class FizzBuzz
    {
        public const int DEFAULT_START_RANGE = 1;
        public const int DEFAULT_END_RANGE = 100;

        private bool reverseOrder;
        private IFizzBuzzStrategy strategy;
        
        public FizzBuzz():this(new StandardFizzBuzzStrategy())
        { }

        public FizzBuzz(int min, int max):this(new StandardFizzBuzzStrategy(), min, max)
        { }
        
        public FizzBuzz(IFizzBuzzStrategy fizzBuzzer):this(fizzBuzzer, DEFAULT_START_RANGE, DEFAULT_END_RANGE)
        { }

        public FizzBuzz(string fizz, string buzz)
            : this(new GeneralFizzBuzzStrategy(fizz, buzz))
        { }

        public FizzBuzz(string fizz, string buzz, int min, int max)
            : this(new GeneralFizzBuzzStrategy(fizz, buzz), min, max)
        { }

        public FizzBuzz(IFizzBuzzStrategy strategy, int min, int max)
        {
            if (strategy == null)
            {
                throw new ArgumentNullException("strategy");
            }

            this.strategy = strategy;

            reverseOrder = min > max;

            StartRange = min;
            EndRange = max;
        }

        public int StartRange { get; private set; }
        public int EndRange { get; private set; }


        public void Execute(TextWriter writer)
        {
            IEnumerable<string> values = null;
            if (!reverseOrder)
            {
                values = Enumerable.Range(this.StartRange, this.EndRange - this.StartRange + 1).Select(e => strategy.Transform(e));
            }
            else
            {
                values = Enumerable.Range(this.EndRange, this.StartRange - this.EndRange + 1).Select(e => strategy.Transform(this.EndRange + this.StartRange - e));
            }

            foreach (var item in values)
            {
                writer.WriteLine(item);
            }
        }

    }
}
